﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

using ConfigInfoWebPlatform.Nlog;
using FileSecretAssemblyStandard;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using ConfigInfoWebPlatform.API.DBCommon;
using ConfigInfoWebPlatform.Model;
using ConfigInfoWebPlatform.API.BusinessCommon;
using System.IO;
using System.Xml.Linq;
using System.Web;

namespace ConfigInfoWebPlatform.API.Controllers
{
    /// <summary>
    /// 打包发布
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class PackagController : ControllerBase
    {
        readonly LogHelper _log;
        readonly string configRootPath;
        readonly InterfaceFileSecret FileSecret;
        /// <summary>
        /// 打包发布
        /// </summary>
        public PackagController(LogHelper logHelper, IConfiguration configuration, InterfaceFileSecret fileSecret)
        {
            _log = logHelper;
            configRootPath = configuration["AppSetting:configPath"].ToString();
            FileSecret = fileSecret;
        }

        /// <summary>
        /// 打包发布列表
        /// </summary>
        [HttpGet("index")]
        public JsonResult Index(int page, int limit)
        {
            DAL_PackagRecord dAL_PackagRecord = new DAL_PackagRecord("configplatform");
            List<PackagRecord> re = new List<PackagRecord>();
            var list = dAL_PackagRecord.GetPackags();
            if(list!=null && list.Any())
            {
                re=list.Skip((page - 1) * limit).Take(limit).ToList();
            }
            return new JsonResult(new { code = 0, msg = "成功", count = list.Count(), data = re });
        }

        /// <summary>
        /// 获取流程树
        /// </summary>
        /// <returns></returns>
        [HttpGet("getprocesstree")]
        public IActionResult GetProcessTree()
        {
            DAL_PackagRecord packagRecordDal = new DAL_PackagRecord("configplatform");
            var processTree = packagRecordDal.GetProcessTree();
            return new JsonResult(new
            {
                code = 0,
                msg = "success",
                data = processTree
            });
        }

        /// <summary>
        /// 打包下载流程配置文件
        /// </summary>
        /// <returns></returns>
        [HttpPost("packagfile")]
        public IActionResult packagFile(PackagModel packagModel)
        {
            var fileName = $"judgedocsystem{DateTime.Now.ToString("yyMMddHHmm")}.zip";
            var solutionPath = System.Environment.CurrentDirectory.Replace(".API", ".UI");
            string zipName =Path.Combine(solutionPath,"wwwroot\\configfiles",fileName);
            //获取用户信息
            UserInfoModel user = new UserInfoModel();
            var userJsonStr = Base64Helper.DeCodeBase64(65001, Request.Headers["user"]);
            user = Newtonsoft.Json.JsonConvert.DeserializeObject<UserInfoModel>(userJsonStr);
            VersionModel version = new VersionModel();
            var versionJsonStr = Base64Helper.DeCodeBase64(65001, Request.Headers["version"]);
            version = Newtonsoft.Json.JsonConvert.DeserializeObject<VersionModel>(versionJsonStr);
            //添加打包记录
            DAL_PackagRecord dAL_PackagRecord = new DAL_PackagRecord("configplatform");
            PackagRecord record = new PackagRecord();
            record.Id = Snowflake.Instance().GetId().ToString();
            record.Remark = packagModel.Remark;
            record.PackagStatus = 1;
            record.FilePath = zipName;
            record.FileName = fileName;
            record.FileStatus = 1;
            record.UserName = user.Name;
            record.CreateTime = DateTime.Now;
            var result = dAL_PackagRecord.InsertPaskagRecord(record);
            if (result == 1)
            {
                Task.Run(() => {
                    if(packagModel.PackagType=="1")
                    {
                        CreateZip(packagModel, zipName, record, user, version);
                    }
                    else if(packagModel.PackagType == "2")
                    {
                        SpecifyCreateZip(packagModel, zipName, record, user, version);
                    }
                });
            }
            return new JsonResult(new
            {
                code = result > 0 ? 0 : 1,
                msg = "正在打包",
                fileName = fileName
            });
        }

        /// <summary>
        /// 删除打包文件
        /// </summary>
        [HttpPost("deldtefile")]
        public IActionResult DeleteFile(string id)
        {
            int resultCode = 0;
            string resultMsg = "删除失败";
            DAL_PackagRecord dAL_PackagRecord = new DAL_PackagRecord("configplatform");
            var record = dAL_PackagRecord.GetPaskagRecordById(id);
            if(record!=null)
            {
                record.FileStatus = 2;
                resultCode = dAL_PackagRecord.UpdatePaskagRecord(record);
                if (resultCode > 0)
                {
                    if (!string.IsNullOrWhiteSpace(record.FilePath))
                    {
                        if (System.IO.File.Exists(record.FilePath))
                        {
                            try
                            {
                                System.IO.File.Delete(record.FilePath);
                            }
                            catch (Exception ex)
                            {
                                resultCode = 0;
                                resultMsg = ex.Message;
                            }
                            resultMsg = "删除成功";
                        }
                        else
                        {
                            resultMsg = "该文件不存在或已被删除";
                        }
                    }
                }
            }
            else
            {
                resultMsg ="该打包记录不存在或已被删除";
            }
            return new JsonResult(new
            {
                code = resultCode > 0 ? 0 : 1,
                msg = resultMsg
            });
        }

        /// <summary>
        /// DownloadBigFile用于下载大文件，循环读取大文件的内容到服务器内存，然后发送给客户端浏览器  datacollection.zip
        /// </summary>
        [HttpGet("DownFile")]
        public async Task<IActionResult> DownloadBigFile(string filename)
        {
            //要下载的文件地址，这个文件会被分成片段，通过循环逐步读取到ASP.NET Core中，然后发送给客户端浏览器
            //var filePath = AppContext.BaseDirectory + "/filepath/" + filename;
            var filePath = filename;
            var fileName = Path.GetFileName(filePath);//测试文档.xlsx

            int bufferSize = 1024;//这就是ASP.NET Core循环读取下载文件的缓存大小，这里我们设置为了1024字节，也就是说ASP.NET Core每次会从下载文件中读取1024字节的内容到服务器内存中，然后发送到客户端浏览器，这样避免了一次将整个下载文件都加载到服务器内存中，导致服务器崩溃

            Response.ContentType = "application/x-zip-compressed";//由于我们下载的是一个Excel文件，所以设置ContentType为application/vnd.ms-excel
            Response.Headers.Add("Connection", "Keep-Alive");
            var contentDisposition = "attachment;" + "filename=" + HttpUtility.UrlEncode(fileName);//在Response的Header中设置下载文件的文件名，这样客户端浏览器才能正确显示下载的文件名，注意这里要用HttpUtility.UrlEncode编码文件名，否则有些浏览器可能会显示乱码文件名
            Response.Headers.Add("Content-Disposition", new string[] { contentDisposition });

            //使用FileStream开始循环读取要下载文件的内容
            using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            {
                using (Response.Body)//调用Response.Body.Dispose()并不会关闭客户端浏览器到ASP.NET Core服务器的连接，之后还可以继续往Response.Body中写入数据
                {
                    long contentLength = fs.Length;//获取下载文件的大小
                    Response.ContentLength = contentLength;//在Response的Header中设置下载文件的大小，这样客户端浏览器才能正确显示下载的进度
                    byte[] buffer;
                    long hasRead = 0;//变量hasRead用于记录已经发送了多少字节的数据到客户端浏览器
                    //如果hasRead小于contentLength，说明下载文件还没读取完毕，继续循环读取下载文件的内容，并发送到客户端浏览器
                    while (hasRead < contentLength)
                    {
                        //HttpContext.RequestAborted.IsCancellationRequested可用于检测客户端浏览器和ASP.NET Core服务器之间的连接状态，如果HttpContext.RequestAborted.IsCancellationRequested返回true，说明客户端浏览器中断了连接
                        if (HttpContext.RequestAborted.IsCancellationRequested)
                        {
                            //如果客户端浏览器中断了到ASP.NET Core服务器的连接，这里应该立刻break，取消下载文件的读取和发送，避免服务器耗费资源
                            break;
                        }
                        buffer = new byte[bufferSize];
                        int currentRead = fs.ReadAsync(buffer, 0, bufferSize).Result;//从下载文件中读取bufferSize(1024字节)大小的内容到服务器内存中
                        await Response.Body.WriteAsync(buffer, 0, currentRead);//发送读取的内容数据到客户端浏览器
                        await Response.Body.FlushAsync();//注意每次Write后，要及时调用Flush方法，及时释放服务器内存空间
                        hasRead += currentRead;//更新已经发送到客户端浏览器的字节数
                        await Task.Delay(100);
                    }
                }
            }
            return new EmptyResult();
        }
        
        /// <summary>
        /// 全流程打包
        /// </summary>
        /// <param name="dirs">打包目录</param>
        /// <param name="fileName">压缩文件名称</param>
        /// <param name="record">打包记录</param>
        /// <param name="user">用户信息</param>
        /// <param name="version">版本信息</param>
        private void CreateZip(PackagModel dirs, string fileName,PackagRecord record,UserInfoModel user, VersionModel version)
        {
            var versionPath = version?.Catalogue == "dev"
                                ? ""
                                : version?.Catalogue;
            string destRootDir = Path.Combine(System.Environment.CurrentDirectory, $"zip{DateTime.Now.ToString("yyyyMMddhhmmss")}");
            string destZipDir = Path.Combine(destRootDir, "config", "wordparse2.0");
            if (!Directory.Exists(destZipDir))
                Directory.CreateDirectory(destZipDir);

            //全流程配置
            var rootPath = Path.Combine(configRootPath, versionPath);
            var wholeFile = $"{rootPath}/config/wholeprocess.xml";
            if (System.IO.File.Exists(wholeFile))
            {
                var wholeContent = System.IO.File.ReadAllText(wholeFile);
                if(!string.IsNullOrWhiteSpace(wholeContent))
                {
                    XDocument xdocument = XDocument.Parse(wholeContent);
                    var rootNode = xdocument.Element("root");
                    foreach (var dir in dirs.Dirs)
                    {
                        var processNodes = rootNode.Descendants()
                                                  .Where(x => x.Name.LocalName == "process")
                                                  .Where(x => x.Attribute("path").Value.Contains(dir))
                                                  .ToList();

                        string sourceDir = Path.Combine(configRootPath, versionPath, dir);
                        string destDir = sourceDir.Replace(configRootPath, destZipDir);
                        //流程配置
                        if (processNodes != null && processNodes.Any())
                        {
                            foreach (var processNode in processNodes)
                            {
                                string processPath = $"{rootPath}/{processNode.Attribute("path").Value}";
                                if (System.IO.File.Exists(processPath))
                                {
                                    FileInfo processFile = new FileInfo(processPath);
                                    var processDestPath = processFile.FullName.Replace(rootPath.Replace("/", "\\"), destZipDir);
                                    var processDestFloder = Path.GetDirectoryName(processDestPath);
                                    if (!Directory.Exists(processDestFloder))
                                    {
                                        System.IO.Directory.CreateDirectory(processDestFloder);
                                    }
                                    if (!System.IO.File.Exists(processDestPath))
                                    {
                                        processFile.CopyTo(processDestPath);//复制文件
                                    }
                                }

                                //模块配置
                                var moduleNodes = processNode.Descendants()
                                                        .Where(x => x.Name.LocalName == "module")
                                                        .ToList();
                                if (moduleNodes != null && moduleNodes.Any())
                                {
                                    foreach (var moduleNode in moduleNodes)
                                    {
                                        string modulePath = $"{rootPath}/{moduleNode.Attribute("path").Value}";
                                        if (System.IO.File.Exists(modulePath))
                                        {
                                            FileInfo moduleFile = new FileInfo(modulePath);
                                            var moduleDestPath = moduleFile.FullName.Replace(rootPath.Replace("/", "\\"), destZipDir);
                                            var moduleDestFloder = Path.GetDirectoryName(moduleDestPath);
                                            if (!Directory.Exists(moduleDestFloder))
                                            {
                                                System.IO.Directory.CreateDirectory(moduleDestFloder);
                                            }
                                            if (!System.IO.File.Exists(moduleDestPath))
                                            {
                                                moduleFile.CopyTo(moduleDestPath);//复制文件
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                    //公共配置文件
                    var commonSourcePath = Path.Combine(rootPath.Replace("/", "\\"), "config\\moduleconfig\\commonconfig");
                    var commonDestPath = commonSourcePath.Replace(rootPath.Replace("/", "\\"), destZipDir);
                    CopyFolder(commonSourcePath, commonDestPath, rootPath);
                }
            }
            ZipHelper.CreateZip(destRootDir, fileName);
            DAL_PackagRecord dAL_PackagRecord = new DAL_PackagRecord("configplatform");
            var result = dAL_PackagRecord.UpdatePaskagStatus(record.Id, 2);
            Task.Run(() => { DeleteFolder(Path.Combine(destRootDir)); });
        }

        /// <summary>
        /// 指定目录打包压缩
        /// </summary>
        /// <param name="packag">打包目录</param>
        /// <param name="fileName">压缩文件名称</param>
        /// <param name="record">打包记录</param>
        /// <param name="user">用户信息</param>
        /// <param name="version">版本信息</param>
        private void SpecifyCreateZip(PackagModel packag, string fileName, PackagRecord record,UserInfoModel user, VersionModel version)
        {
            var versionPath = version?.Catalogue == "dev"
                                ? ""
                                : version?.Catalogue;
            //全流程配置
            var rootPath = Path.Combine(configRootPath, versionPath);
            string destRootDir = Path.Combine(System.Environment.CurrentDirectory, $"zip{DateTime.Now.ToString("yyyyMMddhhmmss")}");
            string destZipDir = Path.Combine(destRootDir, "config", "wordparse2.0");
            if (!Directory.Exists(destZipDir))
                Directory.CreateDirectory(destZipDir);
            foreach (var dir in packag.Dirs)
            {
                string sourceDir = Path.Combine(configRootPath, versionPath, dir);
                string destDir = sourceDir.Replace(configRootPath, destZipDir);
                CopyFolder(sourceDir, destDir, rootPath);
            }
            ZipHelper.CreateZip(destRootDir, fileName);
            DAL_PackagRecord dAL_PackagRecord = new DAL_PackagRecord("configplatform");
            var result = dAL_PackagRecord.UpdatePaskagStatus(record.Id, 2);
            Task.Run(() => { DeleteFolder(destRootDir); });
        }

        /// <summary>
        /// 复制文件夹及文件
        /// </summary>
        /// <param name="sourceFolder">原文件路径</param>
        /// <param name="destFolder">目标文件路径</param>
        private int CopyFolder(string sourceFolder, string destFolder,string rootPath)
        {
            try
            {
                //如果目标路径不存在,则创建目标路径
                if (!System.IO.Directory.Exists(destFolder))
                {
                    System.IO.Directory.CreateDirectory(destFolder);
                }
                //得到原文件根目录下的所有文件
                string[] files = System.IO.Directory.GetFiles(sourceFolder);
                foreach (string file in files)
                {
                    string name = System.IO.Path.GetFileName(file);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    string exName = Path.GetExtension(file);
                    if(!System.IO.File.Exists(dest)&& !exName.ToLower().Contains("back"))
                    {
                        System.IO.File.Copy(file, dest);//复制文件
                    }
                }
                //得到原文件根目录下的所有文件夹
                string[] folders = System.IO.Directory.GetDirectories(sourceFolder);
                foreach (string folder in folders)
                {
                    string name = System.IO.Path.GetFileName(folder);
                    string dest = System.IO.Path.Combine(destFolder, name);
                    CopyFolder(folder, dest,rootPath);//构建目标路径,递归复制文件
                }
                return 1;
            }
            catch (Exception e)
            {
                return 0;
            }

        }

        /// <summary>
        /// 删除目录
        /// </summary>
        /// <param name="dir">要删除的目录</param>
        private void DeleteFolder(string dir)
        {
            if (System.IO.Directory.Exists(dir))
            {
                string[] fileSystemEntries = System.IO.Directory.GetFileSystemEntries(dir);
                for (int i = 0; i < fileSystemEntries.Length; i++)
                {
                    string text = fileSystemEntries[i];
                    if (System.IO.File.Exists(text))
                    {
                        System.IO.File.Delete(text);
                    }
                    else
                    {
                        DeleteFolder(text);
                    }
                }
                System.IO.Directory.Delete(dir);
            }
        }

        /// <summary>
        /// 调整流程配置模块节点路径
        /// </summary>
        private void UpdateDir()
        {
            var wholeFile = $"{configRootPath}/config/wholeprocess.xml";

            if (System.IO.File.Exists(wholeFile))
            {
                var wholeContent = System.IO.File.ReadAllText(wholeFile);
                if (!string.IsNullOrWhiteSpace(wholeContent))
                {
                    XDocument wholeDoc = XDocument.Parse(wholeContent);
                    var processRoot = wholeDoc.Element("root");

                    var processList = processRoot.Descendants().Where(x => x.Name.LocalName == "process").ToList();
                    if(processList!=null && processList.Any())
                    {
                        foreach (var process in processList)
                        {
                            try
                            {
                                var itemPath = process.Attribute("path").Value;
                                string processPath = $"{configRootPath}/{itemPath}";
                                if (System.IO.File.Exists(processPath))
                                {
                                    string configContent = FileSecret.DecryptFileToContent(processPath);
                                    if(!string.IsNullOrWhiteSpace(configContent))
                                    {
                                        XDocument xdocument = XDocument.Parse(configContent);
                                        XElement root = xdocument.Element("root");
                                        if (root != null)
                                        {
                                            var setpList = root.Descendants().Where(x => x.Name.LocalName == "step");
                                            if (setpList != null && setpList.Any())
                                            {
                                                foreach (var step in setpList)
                                                {
                                                    step.Attribute("executefile").Value = step.Attribute("executefile").Value.Replace("\\", "/");
                                                    var groupAttr = step.Attribute("groupstepcode");
                                                    if (groupAttr == null)
                                                    {
                                                        step.SetAttributeValue("groupstepcode", "");
                                                    }
                                                }
                                            }

                                        }
                                        xdocument.Save(processPath);
                                        this.EncryptFileToFile(processPath, processPath);
                                    }
                                    
                                }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                            }
                            
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 文件加密
        /// </summary>
        private void EncryptFileToFile(string inFileName, string outFileName)
        {
            if (inFileName == outFileName)
            {
                string enFileName = Path.GetDirectoryName(inFileName) + "_" + Path.GetFileName(inFileName);
                FileSecret.EncryptFile(inFileName, enFileName);
                System.IO.File.Delete(inFileName);
                System.IO.File.Copy(enFileName, inFileName);
                System.IO.File.Delete(enFileName);
            }
            else
            {
                FileSecret.EncryptFile(inFileName, outFileName);
            }
        }
    }
}
